package com.example.opentiktok;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.content.Context;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.widget.Toast;

import androidx.annotation.RequiresApi;


import java.util.List;

public abstract class AccessibilityOperator {
    //视频号列表选择作品，最大重试次数
    public int maxNum = 0;

    protected Context mContext;
    public AccessibilityEvent mAccessibilityEvent;
    public AccessibilityService mAccessibilityService;
    private  Point point;

    public void setContext(Context context) {
        mContext = context;
        point = AppUtils.getScreenWidth(mContext);
//        LogUtil.d("point.x : " + point.x);
//        LogUtil.d("point.y : " + point.y);
    }

    public void setAccessibilityService(AccessibilityService accessibilityService) {
//        if (accessibilityService != null && mAccessibilityService == null) {
//            mAccessibilityService = accessibilityService;
//        }

        // 当服务重新启动时，两个service不一致，会导致流程无法继续，所以需要重新设置service
        if(accessibilityService != null && accessibilityService != mAccessibilityService){
            mAccessibilityService = accessibilityService;
        }


    }

    public void setAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
        mAccessibilityEvent = accessibilityEvent;
    }

    /**
     * 做任务
     * @param orderId               订单id
     * @param start                 是否开始
     * @param className             类名
     * @param kind                  订单类型
     * @param isDouble              是否双击
     */
    public abstract void doAction(long orderId, boolean start, String className, int kind, boolean isDouble);


    private AccessibilityNodeInfo getRootNodeInfo() {
        AccessibilityEvent curEvent = mAccessibilityEvent;
        AccessibilityNodeInfo nodeInfo = null;
        if (Build.VERSION.SDK_INT >= 16) {
            // 建议使用getRootInActiveWindow，这样不依赖当前的事件类型
            if (mAccessibilityService != null) {
                nodeInfo = mAccessibilityService.getRootInActiveWindow();
            }
            // 下面这个必须依赖当前的AccessibilityEvent
//            nodeInfo = curEvent.getSource();
        } else {
            nodeInfo = curEvent.getSource();
        }
        return nodeInfo;
    }

    public List<AccessibilityNodeInfo> findNodesById(String viewId) {
        AccessibilityNodeInfo nodeInfo = getRootNodeInfo();
        if (nodeInfo != null) {
            if (Build.VERSION.SDK_INT >= 18) {
                return nodeInfo.findAccessibilityNodeInfosByViewId(viewId);
            }
        }
        return null;
    }

    /**
     * 根据View的ID搜索符合条件的节点,精确搜索方式;
     * 这个只适用于自己写的界面，因为ID可能重复
     * api要求18及以上
     *
     * @param viewId
     * @return 是否点击成功
     */
    public boolean clickById(String viewId) {
        return performClick(findNodesById(viewId));
    }

    private boolean performClick(List<AccessibilityNodeInfo> nodeInfos) {
        if (nodeInfos != null && !nodeInfos.isEmpty()) {
            AccessibilityNodeInfo node;
            for (int i = 0; i < nodeInfos.size(); i++) {
                node = nodeInfos.get(i);
                // 进行模拟点击
                if (node.isClickable()) {
                    LogUtil.d("Click：" + node.getClassName());
                    return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                } else if (node.getParent().isClickable()) {
                    LogUtil.d("Click：" + node.getParent().getClassName());
                    return node.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }
        return false;
    }

    public boolean longClickById(String viewId) {
        return performLongClick(findNodesById(viewId));
    }

    private boolean performLongClick(List<AccessibilityNodeInfo> nodeInfos) {
        if (nodeInfos != null && !nodeInfos.isEmpty()) {
            AccessibilityNodeInfo node;
            for (int i = 0; i < nodeInfos.size(); i++) {
                node = nodeInfos.get(i);
                // 进行模拟点击
                if (node.isClickable()) {
                    LogUtil.d("Long Click：" + node.getClassName());
                    return node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
                } else if (node.getParent().isClickable()) {
                    LogUtil.d("Long Click：" + node.getParent().getClassName());
                    return node.getParent().performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
                }
            }
        }
        return false;
    }

    public boolean clickBackKey() {
        return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
    }

    private boolean performGlobalAction(int action) {
        return mAccessibilityService.performGlobalAction(action);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    public void scoll(int x, int y) {
        GestureDescription.Builder builder = new GestureDescription.Builder();
        Path path = new Path();
        path.moveTo(x, y);
        path.lineTo(100, 0);
        GestureDescription gestureDescription = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 200L, 800L, false)).build();
        }
        mAccessibilityService.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
            @Override
            public void onCompleted(GestureDescription gestureDescription) {
                super.onCompleted(gestureDescription);
            }

            @Override
            public void onCancelled(GestureDescription gestureDescription) {
            }
        }, new Handler(Looper.getMainLooper()));

    }

    /**
     * 根据Text搜索所有符合条件的节点, 模糊搜索方式
     */
    public List<AccessibilityNodeInfo> findNodesByText(String text) {
        AccessibilityNodeInfo nodeInfo = getRootNodeInfo();
        if (nodeInfo != null) {
            return nodeInfo.findAccessibilityNodeInfosByText(text);
        }
        return null;
    }

    public boolean clickByText(String text) {
        return performClick(findNodesByText(text));
    }

    //视频号双击点赞
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void doubleClickWx(boolean isShow){
        //此为2次事件，不连续
        if (mAccessibilityService == null) return;
        try {
            LogUtil.d("wdd---  执行双击点赞  ");
            Path path = new Path();
            Point point = AppUtils.getScreenWidth(mContext);
            path.moveTo(point.x == 0 ? 200 : point.x / 4, point.y == 0 ? 400 : point.y / 2);
            final GestureDescription.StrokeDescription sd;
            sd = new GestureDescription.StrokeDescription(path, 10, 100);
            mAccessibilityService.dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback() {
                @Override
                public void onCompleted(GestureDescription gestureDescription) {
                    super.onCompleted(gestureDescription);
                    LogUtil.d("2222222successEmpty 99999 : 开始双击点赞了 ");
                    if (isShow){

                    }
                    //也可以使用handler延时1.5秒就不用在这里回调了
                    Path path2 = new Path();
                    path2.moveTo(point.x == 0 ? 200 : point.x / 4, point.y == 0 ? 400 : point.y / 2);
                    final GestureDescription.StrokeDescription sd2 = new GestureDescription.StrokeDescription(path2, 100, 100);
                    //滑完后再过1秒竖滑
                    mAccessibilityService.dispatchGesture(new GestureDescription.Builder().addStroke(sd2).addStroke(sd2).build(), null, null);
                }

                @Override
                public void onCancelled(GestureDescription gestureDescription) {
                    super.onCancelled(gestureDescription);
                }
            }, null);

        }catch (Exception ex){
            LogUtil.d("双击操作 ex : " + ex.getMessage());
        }
    }
    //双击点赞
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void   doubleClick(boolean isShow){

        //此为2次事件，不连续
        if (mAccessibilityService == null) return ;
        try {
            LogUtil.d("wdd---  执行双击点赞  ");
            Path path = new Path();
            Point point = AppUtils.getScreenWidth(mContext);
            path.moveTo(point.x == 0 ? 200 : point.x / 4, point.y == 0 ? 400 : point.y / 4);
            final GestureDescription.StrokeDescription sd;
            sd = new GestureDescription.StrokeDescription(path, 10, 100);
            mAccessibilityService.dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback() {
                @Override
                public void onCompleted(GestureDescription gestureDescription) {
                    super.onCompleted(gestureDescription);
                    LogUtil.d("2222222successEmpty 99999 : 开始双击点赞了 ");
                    if (isShow){

                    }
                    //也可以使用handler延时1.5秒就不用在这里回调了
                    Path path2 = new Path();
                    path2.moveTo(point.x == 0 ? 200 : point.x / 4, point.y == 0 ? 400 : point.y / 4);
                    final GestureDescription.StrokeDescription sd2 = new GestureDescription.StrokeDescription(path2, 100, 100);
                    //滑完后再过1秒竖滑
                     mAccessibilityService.dispatchGesture(new GestureDescription.Builder().addStroke(sd2).addStroke(sd2).build(), null, null);
                }

                @Override
                public void onCancelled(GestureDescription gestureDescription) {
                    super.onCancelled(gestureDescription);
                }
            }, null);

        }catch (Exception ex){
            LogUtil.d("双击操作 ex : " + ex.getMessage());
        }
    }

    /**
     * 从指定的节点开始向下查找指定类名的组件（深度遍历），在找到一个符合之后就会结束
     *
     * @param nodeInfo   起始节点
     * @param classNames 类名（可多个），每进行一次节点的深度遍历，都会遍历一遍这里传入来的类名，找到了就立即返回
     *
     * @return 最后找到的节点
     */
    @RequiresApi(Build.VERSION_CODES.N)
    protected AccessibilityNodeInfo getNodeByClassName( AccessibilityNodeInfo nodeInfo, String... classNames) {
        if (nodeInfo.getChildCount() == 0) {
            return null;
        }
        for (int i = 0; i < nodeInfo.getChildCount(); i++) {
            AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
            if (BuildConfig.DEBUG) {
                StringBuilder sb = new StringBuilder(classNames.length);
                for (String className : classNames) {
                    sb.append(className).append(" ");
                }
                LogUtil.d( "index: %d className: %s target: %s" + i + childNodeInfo.getClassName().toString() + sb.toString());
            }
            for (String className : classNames) {
                if (childNodeInfo.getClassName().toString().equals(className)) {
                    return childNodeInfo;
                }
            }
            AccessibilityNodeInfo switchOrCheckBoxNodeInfo = getNodeByClassName(childNodeInfo, classNames);
            if (switchOrCheckBoxNodeInfo != null) {
                return switchOrCheckBoxNodeInfo;
            }
        }
        return null;
    }

    /**
     * 从根节点节点开始向下查找指定类名的组件（深度遍历），在找到一个符合之后就会结束
     *
     * @param classNames 类名（可多个），每进行一次节点的深度遍历，都会遍历一遍这里传入来的类名，找到了就立即返回
     *
     * @return 最后找到的节点
     */
    @RequiresApi(Build.VERSION_CODES.N)
    public AccessibilityNodeInfo getNodeByClassName( String... classNames) {
        AccessibilityNodeInfo result;
        AccessibilityNodeInfo rootNodeInfo = mAccessibilityService.getRootInActiveWindow();
        if (rootNodeInfo == null) {
            List<AccessibilityWindowInfo> windowList = mAccessibilityService.getWindows();
            for (AccessibilityWindowInfo window : windowList){
                AccessibilityNodeInfo rootNode = window.getRoot();
                result = getNodeByClassName(rootNode, classNames);
                rootNode.recycle();
                return result;
            }
            return null;
        }else{
            result = getNodeByClassName(rootNodeInfo, classNames);
            rootNodeInfo.recycle();
            return result;
        }
    }

}
